package com.bioscript.standalone.IO;

import java.io.File;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.LinkedList;

import com.bioscript.standalone.IO.GbkParsingEvents.GBKEventTypes;
import com.bioscript.standalone.IO.baseClasses.BaseListener;
import com.bioscript.standalone.IO.baseClasses.EventType;
import com.bioscript.standalone.IO.baseClasses.BaseEvent.BaseEventTypes;

public class GbkFileParser<E> extends BaseListener<GbkParsingEvents>   
{
	private String featureName = null;
	private String positionalInfo = null;
	private String featureRef = null;
	private String translation = null;
	public String getTranslation() {
		return translation;
	}

	private EventType type = null;
	private RandomAccessFile accessFile = null;
	private long seqPosition = 0l;
	private static long offSet = 76;
	private boolean stopReading = false;

	private LinkedList<BaseListener<GbkParsingEvents>> eventListeners = 
		new LinkedList<BaseListener<GbkParsingEvents>>();

	public void addListener(BaseListener<GbkParsingEvents> gbkParsingListener)
	{
		eventListeners.add(gbkParsingListener);
	}

	private void fireEvent(GbkParsingEvents parseEvent)
	{
		for (BaseListener<GbkParsingEvents> listener : eventListeners)
		{
			listener.handleEvent(parseEvent);
		}
	}

	public void parse(File file) throws IOException
	{
		addListener(this);
		accessFile = new RandomAccessFile(file, "r");
		fireEvent(new GbkParsingEvents(null,BaseEventTypes.docStarted));
		String readLine = accessFile.readLine();
		while(readLine != null && !stopReading)
		{
			readLine = readLine.trim();
			if (readLine.startsWith("gene  "))
			{
				type = GBKEventTypes.gene;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				//System.out.println(featureName);
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			else if (readLine.startsWith("CDS  "))
			{
				type = GBKEventTypes.cds;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				if (positionalInfo.startsWith("<"))
					positionalInfo = positionalInfo.substring(1);
				if (positionalInfo.endsWith(">"))
					positionalInfo = positionalInfo.substring(0,positionalInfo.length() - 1);
				
				// find the name of the CDS
				// first try to get the gene name and then the protein id
				while(true)
				{
					readLine = accessFile.readLine();
					if (readLine.trim().startsWith("/gene") || readLine.trim().startsWith("/protein_id"))
					{
						featureName = readLine.trim().split("=")[1].replaceAll("\"", "");
						break;
					}
					if (readLine.trim().startsWith("/translation") || readLine.trim().startsWith("/db_xref"))
					{
						featureName = "unKnown";
						break;
					}
				}
				
				//System.out.println(featureName);
				boolean translationStarted = false;
				while(true)
				{
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
					}
					else if (readLine.startsWith("/translation"))
					{
						String temp = readLine.split("\"")[1];
						if (readLine.endsWith("\""))
						{
							translation = temp.substring(0,temp.length() - 1);
							break;
						}
						else
						{
							translation = temp;
							translationStarted = true;
						}
					}
					if (translationStarted)
					{
						readLine = readLine.trim();
						if (!readLine.startsWith("/translation"))
						{
							if (readLine.endsWith("\""))
							{
								translation = translation + readLine.substring(0,readLine.length() - 1);
								break;
							}
							else
							{
								translation = translation + readLine;
							}
						}
					}
					readLine = accessFile.readLine().trim();
				}
				fireEvent(new GbkParsingEvents(readLine, type));
				translation = null;
			}
			else if (readLine.startsWith("tRNA  "))
			{
				type = GBKEventTypes.tRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			else if (readLine.startsWith("rRNA  "))
			{
				type = GBKEventTypes.rRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = accessFile.readLine().trim().split("=")[1].replaceAll("\"", "");
				while(true)
				{
					readLine = accessFile.readLine().trim();
					if (readLine.startsWith("/db_xref="))
					{
						featureRef = readLine.split("=")[1].replaceAll("\"", "");
						break;
					}
				}
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			else if (readLine.startsWith("ncRNA  "))
			{
				type = GBKEventTypes.ncRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = null;
				featureRef = null;
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			else if (readLine.startsWith("tmRNA  "))
			{
				type = GBKEventTypes.tmRNA;
				String[] params = readLine.split(" ");
				positionalInfo = params[params.length - 1]; 
				featureName = null;
				featureRef = null;
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			else if (readLine.startsWith("ORIGIN"))
			{
				type = GBKEventTypes.sequenceStart;
				seqPosition = accessFile.getChannel().position();
				fireEvent(new GbkParsingEvents(readLine, type));
				//				break;
			}
			else if (readLine.startsWith("ACCESSION"))
			{
				type = GBKEventTypes.accessionNumber;
				fireEvent(new GbkParsingEvents(readLine,type));
			}
			else if (type == GBKEventTypes.sequenceStart || type == GBKEventTypes.sequence)
			{
				type = GBKEventTypes.sequence;
				fireEvent(new GbkParsingEvents(readLine, type));
			}
			readLine = accessFile.readLine();
		}
		fireEvent(new GbkParsingEvents(null,BaseEventTypes.docEnded));
	}

	public String getGeneAtPosition(long startPos, long endPos) throws IOException
	{
		StringBuffer buffer = new StringBuffer();
		long startlinenumber = 0l;
		long endlinenumber = 0l;
		if (startPos % 60 == 0) startlinenumber = (startPos / 60) - 1 ;
		else startlinenumber = startPos / 60;
		if (endPos % 60 == 0) endlinenumber = (endPos / 60) - 1 ;
		else endlinenumber = endPos / 60;

		accessFile.getChannel().position(seqPosition + startlinenumber * offSet);

		for (long j = startlinenumber; j <= endlinenumber ; j++)
		{
			StringBuffer temp = new StringBuffer();
			String readLine = accessFile.readLine().trim();
			//			System.out.println(readLine);
			String[] params = readLine.split(" ");
			for (int i = 1; i< params.length ; i++)
			{
				temp.append(params[i]);
			}
			long startlineOffSet = startPos % 60;
			if (startlineOffSet == 0)
				startlineOffSet = 60;
			long endlineOffSet = 60;
			if (j == endlinenumber)
			{
				endlineOffSet = endPos % 60;
				if (endlineOffSet == 0)
					endlineOffSet = 60;
			}
			if (startlineOffSet > endlineOffSet)
			{
				System.out.println(readLine);
				System.exit(1);
			}
			buffer.append(temp.substring((int)startlineOffSet - 1, (int)endlineOffSet));
			startPos = startPos + (60 - startlineOffSet) + 1;
		}

		return buffer.toString();
	}

	public String getFeatureName()
	{
		return featureName;
	}

	public String getPositionalInfo()
	{
		return positionalInfo;
	}

	public String getFeatureRef()
	{
		return featureRef;
	}

	public void setStopReading(boolean stopReading)
	{
		this.stopReading = stopReading;
	}

	@Override
	public void handleEvent(GbkParsingEvents event) {
		System.err.println("One should override the method handleEvent");
		System.exit(-1);
	}
}
